#include "Includes.h"
#include "DRV_Systick.h"

/*
 * Systick select external clock source
 * value is 0.5s clock count number
 * User overwrite the macro define value
 */
#define SYSTICK_EXT_CLOCK_500MS_CNT (72000000/8/2)

volatile static uint32_t systick_ms = 0;
volatile static uint32_t systick_us = 0;
volatile static uint8_t is_init = 0;

void systick_init(void)
{
  __disable_irq();
  is_init = 1;
  SysTick->LOAD  = SYSTICK_EXT_CLOCK_500MS_CNT; 
  SysTick->VAL   = SYSTICK_EXT_CLOCK_500MS_CNT;
  SysTick->CTRL  = SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk;
  __enable_irq();
}

void systick_deinit(void)
{
  __disable_irq();
  is_init = 0;
  SysTick->CTRL  &= (~(SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk));
  __enable_irq();
}

void delay_us(uint32_t us)
{
  if (!is_init) systick_init();

  uint32_t end_count = 0, current = 0;

  current = get_systick_us();
  end_count = current + us;

  if (end_count == current) {
    return;
  } else if (end_count > current) {
    while (get_systick_us() < end_count);
  } else {
    while (get_systick_us() >= current);
    while (get_systick_us() < end_count);
  }
}

void delay_ms(uint32_t ms)
{
  if(!is_init) systick_init();

  uint32_t end_count = 0, current = 0;

  current = get_systick_ms();
  end_count = current + ms;

  if (end_count == current) {
    return;
  } else if (end_count > current) {
    while (get_systick_ms() < end_count);
  } else {
    while (get_systick_ms() >= current);
    while (get_systick_ms() < end_count);
  }
}

/*
 * Max 4294967295(us)=4294967(us)=4294(s)=71(min)
 */
uint32_t get_systick_us(void)
{
  if (!is_init) systick_init();

  uint32_t _systick_us, _val;

  do {
    _systick_us = systick_us;
    _val = SysTick->VAL;
  } while (_systick_us != systick_us);

#if 0
  /*
   * 64bit, low efficiency 
   */
  uint64_t tick = (SYSTICK_EXT_CLOCK_500MS_CNT - _val); 
  uint32_t tick_us =(tick * 1000 * 500) / SYSTICK_EXT_CLOCK_500MS_CNT;
#else
  /*
   * 32bit, high efficiency 
   */
  uint32_t tick = (SYSTICK_EXT_CLOCK_500MS_CNT - _val);
  uint32_t tick_us =(tick * 200) / (SYSTICK_EXT_CLOCK_500MS_CNT / 2500);
#endif

  return (tick_us + _systick_us);
}

/*
 * Max 4294967295(ms)=4294967(s)=71582(min)=1193(hour)=49(day)
 */
uint32_t get_systick_ms(void)
{
  if (!is_init) systick_init();

  uint32_t _systick_ms, _val;

  do {
    _systick_ms = systick_ms;
    _val = SysTick->VAL;
  } while (_systick_ms != systick_ms);

  uint32_t tick = (SYSTICK_EXT_CLOCK_500MS_CNT - _val);
  uint32_t tick_ms = tick / (SYSTICK_EXT_CLOCK_500MS_CNT / 500);
  tick_ms = tick_ms + _systick_ms;

  return tick_ms ;
}

/*
 * Call by Systick interrupt server
 */
void systick_handler(void)
{
  systick_us += 500 * 1000;
  systick_ms += 500;
}


extern void xPortSysTickHandler(void);

/**
 * @brief     systick中断服务函数,使用OS时用到
 * @param     ticks : 延时的节拍数  
 * @retval    无
 */  
void SysTick_Handler(void)
{
    systick_handler();
    if(systick_ms%1000 == 0)
    {
        /* OS 开始跑了,才执行正常的调度处理 */
        if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
        {
            xPortSysTickHandler();
        }
    }
}

